package com.kun.core;

import com.kun.utils.Resources;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import javax.sql.DataSource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * SqlSessionFactory 构建器
 * @author fangkun
 * @create 2023-09-20-17:00
 */
public class SqlSessionFactoryBuilder {

    public SqlSessionFactoryBuilder(){}

    /**
     * 解析 xml 配置文件，构建 SqlSessionFactory
     * @param in
     * @return
     */
    public SqlSessionFactory build(InputStream in){
        SqlSessionFactory sqlSessionFactory = null;
        try {
            //获取document
            SAXReader reader = new SAXReader();
            Document document = reader.read(in);
            //environments
            Element environments = (Element) document.selectSingleNode("/configuration/environments");
            String defaultId = environments.attributeValue("default");
            //根据 defaultId 获取 environment
            Element environment = (Element)document.selectSingleNode("/configuration/environments/environment[@id='" + defaultId + "']");
            //事务管理器 Element
            Element transactionElt = environment.element("transactionManager");
            //数据源 Element
            Element dataSourceElt = environment.element("dataSource");
            //mapper 标签
            List<Node> nodes = document.selectNodes("//mapper");
            //mapper 路径集合
            List<String> sqlMapperXMLPathList = new ArrayList<>();
            nodes.forEach(node -> {
                Element mapper = (Element) node;
                String resource = mapper.attributeValue("resource");
                sqlMapperXMLPathList.add(resource);
            });

            // 获取数据源对象
            DataSource dataSource = getDataSource(dataSourceElt);
            // 获取事务管理器
            Transaction transaction = getTransaction(transactionElt,dataSource);
            // 获取mappedStatements
            Map<String, MappedStatement> mappedStatements = getMappedStatements(sqlMapperXMLPathList);
            //构建sqlSessionFactory对象
            sqlSessionFactory = new SqlSessionFactory(transaction,mappedStatements);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return sqlSessionFactory;
    }

    /**
     * 解析所有的SqlMapper.xml文件，然后构建Map集合。
     * @param sqlMapperXMLPathList
     * @return
     */
    private Map<String, MappedStatement> getMappedStatements(List<String> sqlMapperXMLPathList) {
        Map<String, MappedStatement> mappedStatements = new HashMap<>();
        sqlMapperXMLPathList.forEach(sqlMapperXMLPath -> {
            try {
                //读取mapper文件
                SAXReader reader = new SAXReader();
                Document document = reader.read(Resources.getResourcesAsStream(sqlMapperXMLPath));
                Element mapper = ((Element) document.selectSingleNode("mapper"));
                //namespace
                String namespace = mapper.attributeValue("namespace");
                List<Element> elements = mapper.elements();
                elements.forEach(element -> {
                    String id = element.attributeValue("id");
                    //组装mapperId
                    String sqlId = namespace + "." + id;
                    String resultType = element.attributeValue("resultType");
                    String sql = element.getTextTrim();

                    //new MappedStatement
                    MappedStatement mappedStatement = new MappedStatement(sql,resultType);
                    mappedStatements.put(sqlId,mappedStatement);
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        return mappedStatements;
    }

    /**
     * 获取事务管理器
     * @param transactionElt
     * @param dataSource
     * @return
     */
    private Transaction getTransaction(Element transactionElt, DataSource dataSource) {

        Transaction transaction = null;

        String transType = transactionElt.attributeValue("type").trim().toUpperCase();
        if (Const.JDBC_TRANSACTION.equals(transType)){
            // 默认是开启事务的，将来需要手动提交的。
            transaction = new JdbcTransaction(dataSource,false);
        }
        if (Const.MANAGED_TRANSACTION.equals(transType)){
            transaction = new ManagedTransaction();
        }

        return transaction;
    }

    /**
     * 获取数据源对象
     * @param dataSourceElt
     * @return
     */
    private DataSource getDataSource(Element dataSourceElt) {

        Map<String,String> map = new HashMap<>();
        //property 属性保存到map
        List<Element> propertyElts = dataSourceElt.elements("property");
        propertyElts.forEach(property -> {
            String name = property.attributeValue("name");
            String value = property.attributeValue("value");
            map.put(name,value);
        });

        String dataSourceType = dataSourceElt.attributeValue("type").trim().toUpperCase();

        DataSource dataSource = null;
        if (Const.UN_POOLED_DATASOURCE.equals(dataSourceType)){
            dataSource = new UnPooledDataSource(map.get("driver"), map.get("url"), map.get("username"), map.get("password"));
        }
        if (Const.POOLED_DATASOURCE.equals(dataSourceType)){
            dataSource = new PooledDataSource();
        }
        if (Const.JNDI_DATASOURCE.equals(dataSourceType)){
            dataSource = new JNDIDataSource();
        }

        return dataSource;
    }
}
